package cn.gwm.flink.streaming.strategy.cloudpredict;

import cn.gwm.flink.streaming.constant.ChargePredictFields;
import cn.gwm.flink.streaming.strategy.vehiclepredict.DwsStrConstant;
import cn.gwm.utils.HBaseDataSource;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.flink.api.common.state.MapState;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;

/**
 * @author : GW00280745
 * @date : 2023/6/28 8:06
 * @description : 云端预测 工况匹配
 */
public class FirstMatch implements Serializable {
    private CloudDefaultVal cloudDefaultVal = new CloudDefaultVal();
    public void process(JSONObject line,MapState<String, String> initState,MapState<String, String> latestEstimateState){
        try {
            //是否插枪后的第一个点
            String chargeIdName = chargeIdName(line);
            String gun1 = chargeIdName.concat("gun1");
            if (initState.contains(gun1)){
                //说明不是第一个点
                dataState(line,null,latestEstimateState);
                return;
            }
            //说明是第一个点则进行匹配
            initState.put(gun1,chargeIdName);
            //进行匹配
            String predict= calOfFirst(line,initState);
            //2. 结果校验
            dataState(line,predict,latestEstimateState);
            //3.添加标记
            line.put("start_point_flag",DwsStrConstant.ins().str1);
            line.put("soc_interval_point",DwsStrConstant.ins().str1);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 状态处理-- 加工结果写道状态中
     *
     *
     * @param line
     * @param predictStr
     */
    private void dataState(JSONObject line, String predictStr
            ,MapState<String, String> latestEstimateState) {
        try {
            //结果校验
            String chargeId = chargeIdName(line);
            String predict = predictStr;
            String keyPredict = ChargePredictFields.ins().stdFieldPredictTime;
            String stateKeyPredict = chargeId.concat(keyPredict);
            String keyMult = ChargePredictFields.ins().stdFieldPredictTimeV2;
            String stateKeyMult = chargeId.concat(keyMult);
            if (StringUtils.isBlank(predictStr)){
                predict = latestEstimateState.get(stateKeyMult);
            }
            //解析计算结果
            String[] split = predict.split(ChargePredictFields.ins().char3);
            String estimateTime = split[2];
            //添加字段--predict_time字段   原本格式= 2022-09-10 17:05:22  临时用它存储每次预测的过程
//            String predictTime = predictTime(line, estimateTime)
            String predictTime = estimateTime;
            line.put(keyPredict, predictTime);
            latestEstimateState.put(stateKeyPredict, predictTime);

            //添加字段-- predict_time_multidimension
            line.put(keyMult, predict);
            latestEstimateState.put(stateKeyMult, predict);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 初始计算
     * @param line
     * @return
     */
    private String calOfFirst(JSONObject line,MapState<String, String> initState) {
        try {
            //先用经验值
            long t1 = System.currentTimeMillis();
            String process = cloudDefaultVal.process(line);
            System.out.println(String.format("chargeId=%s ,  firstmatch  jingyanzhi cost time ==> %s ms ",chargeIdName(line),System.currentTimeMillis() - t1));
            if (StringUtils.isNotBlank(process)){
                line.put("policyType","beginning-modelcode");
                return  process;
            }else {
                return "0#0#-999999";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private String poiSelect(String selectStr, JSONObject line) {
        String poiId = line.getString(ChargePredictFields.ins().srcFieldPoiId);
        String aoiId = line.getString(ChargePredictFields.ins().srcFieldAoiId);
        //这个条件可能有也可能没有,有如下四种情况
        //1 poi=-9  aoi = -9 则没有这个条件
        //2 poi=-9  aoi = A  则条件是 aoi=A
        //3 poi=A   aoi = -9 则条件是 poi=A
        //4 poi=A   aoi = A 则条件是 poi=A || aoi=A
        boolean poiB =  StringUtils.isBlank(poiId) || StringUtils.equalsIgnoreCase(poiId, ChargePredictFields.ins().defaultVal);

        boolean aoiB = StringUtils.isBlank(aoiId) || StringUtils.equalsIgnoreCase(aoiId, ChargePredictFields.ins().defaultVal);
        String res = selectStr;
        if (poiB){
            // poi = -9 或者 空
            if (aoiB){
                //没有这个条件
                res = selectStr.concat(" , \"aoiid\" =\'-9\' as \"poiDim\" ");
            }else {
                //条件是 aoi =A
                res = selectStr.concat(" , \"aoiid\" =\'").concat(aoiId).concat("\' as \"poiDim\" ");
            }
        }else {
            // poi = A
            if (aoiB){
                //条件是 poi = A
                res = selectStr.concat(" , \"poiid\" =\'").concat(poiId).concat("\' as \"poiDim\" ");
            }else {
                //条件是 poi=A || aoi=A
                res = selectStr.concat(" , ( \"poiid\" =\'").concat(poiId).concat("\' or \"aoiid\"=\'").concat(aoiId).concat("\' ) as \"poiDim\" ");
            }
        }
        return res;
    }

    /**
     *  将匹配到的样本进行存储 便于更新阶段读取
     * @param json
     * @param line
     * @param initState
     */
    private void infoOfSample2State(JSONObject json, JSONObject line, MapState<String, String> initState, float timeOfRevise) {
        if (initState==null){
            return;
        }
        String chargeId = chargeIdName(line);
        try {
            //修正时间
            String key1 = chargeId.concat("timeOfRevise");
            initState.put(key1,String.valueOf(timeOfRevise));
            //高效时间
            Long time10 = json.getLong(steadyTime10Key);
            Long time80 = json.getLong(steadyTime80Key);
            Long time100 = json.getLong(steadyTimeEndKey);

            long l = time80 - time10;
            String key2 = chargeId.concat("timeOfEfficient");
            initState.put(key2,String.valueOf(l));
            //涓流时间
            long lg = time100 - time80;
            String key3 = chargeId.concat("timeOfTrickle");
            initState.put(key3,String.valueOf(lg));
            System.out.println(String.format("chargeId=%s ,info_of_stages(cal_first) ---> timeOfRevise=%s ,timeOfEfficient=%s ,timeOfTrickle=%s",chargeId,timeOfRevise,l,lg));
        }catch (Exception e){
            e.printStackTrace();
            System.out.println(String.format("exception:%s, chargeId=%s ", ExceptionUtils.getMessage(e),chargeId));
        }
    }

    private String chargeIdName(JSONObject line){
        String vin = line.getString(ChargePredictFields.ins().srcFieldVin);
        String startChargeTime = line.getString(ChargePredictFields.ins().srcFieldStartChargeTime);
        String chargeId = vin.concat(startChargeTime);
        return chargeId;
    }
    private JSONObject defaultMatch(JSONObject line) {
        //初始计算 如果提取不到对应的记录，以CC码为单位，提取时间最近、差异最小的记录；
        //构建sql语句
        String whereAfterStr = queryOptionModel(line,false);
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);

//        String fromStrModelLocal = String.format(fromStrModel, "dws_charge_detail_slice");
        String fromStrModelLocal = String.format(fromStrModel, "dws_charge_detail_slice_dcobc");
        String dc = line.getString(ChargePredictFields.ins().srcFieldBmsDcConnect);
        if (StringUtils.equalsIgnoreCase(dc,DwsStrConstant.ins().str1)){
//            fromStrModelLocal = String.format(fromStrModel, "dws_charge_detail_slice_dc");
            fromStrModelLocal = String.format(fromStrModel, "dws_charge_detail_slice_dcobc");
        }
        String select = poiSelect(selectStr,line);
        String sqlStr = String.format(select, bmsSoc, bmsSoc).concat(fromStrModelLocal).concat(whereAfterStr);
        List<JSONObject> list = queryExecution(sqlStr,line);
        //2.基于上一步结果匹配电流、地点维度
        JSONObject json= poiIdDimension(line,list);
        if (json==null){
            //去掉 地点维度 再匹配一次  [高度优先本地点的充电记录]
//            whereAfterStr = queryOptionModel(line,false);
//            sqlStr = String.format(selectStr, bmsSoc, bmsSoc).concat(fromStrModelLocal).concat(whereAfterStr);
//            list = queryExecution(sqlStr,line);
//            json= poiIdDimension(line,list);
        }
        return json;
    }

    /**
     *
     * 前端修正时间
     * @param json
     * @param line
     * @return
     */
    private float reviseOfStart(JSONObject json, JSONObject line) {
        //计算起点SOC和参考样本的SOC差的绝对值，参考样本数据SOC充满此差值的时间t2，作为充电时长修订量
        Float innerSocLine = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        Float innerSocSlice = json.getFloat(innerSocKey);
        BigDecimal bigDecimal = json.getBigDecimal(sliceTimeKey);
        float deltaSoc = innerSocSlice - innerSocLine;

        Float socEnd = json.getFloat(steadySocEndKey);
        Float effectiveTime = json.getFloat(effectiveChargeTimeKey);
        Float startSoc = json.getFloat(startSocKey);
        float v = effectiveTime / (socEnd - startSoc);
        float res = new BigDecimal(v).setScale(1, BigDecimal.ROUND_HALF_UP).floatValue();
//        return res*deltaSoc;
        Long sliceTime = bigDecimal.longValueExact();
        return  (sliceTime*deltaSoc);
    }

    private String queryOption(JSONObject line,boolean hasPoiDim){
        //获取当前工况
        String vin = line.getString(ChargePredictFields.ins().srcFieldVin);
        Long tidVal = line.getLong(ChargePredictFields.ins().srcFieldTid);
        String startChargeTime = line.getString(ChargePredictFields.ins().srcFieldStartChargeTime);
        String poiId = line.getString(ChargePredictFields.ins().srcFieldPoiId);
        String aoiId = line.getString(ChargePredictFields.ins().srcFieldAoiId);
        String tempMin = line.getString(ChargePredictFields.ins().srcFieldBmsRmcModuleTempMin);
        String tempMax = line.getString(ChargePredictFields.ins().srcFieldBmsRmcModuleTempMax);
        Float bmsCurr = line.getFloat(ChargePredictFields.ins().srcFieldBmsBatterCurrRm);
        String voltMax = line.getString(ChargePredictFields.ins().srcFieldVoltMax);
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        String modelCode = line.getString(ChargePredictFields.ins().srcFieldModelCode);
        String acOpen = line.getString(ChargePredictFields.ins().srcFieldAcOpen);
        String heat = line.getString(ChargePredictFields.ins().srcFieldBmsHeatReq);
        String cool = line.getString(ChargePredictFields.ins().srcFieldBmsCoolReq);
        String obc = line.getString(ChargePredictFields.ins().srcFieldBmsObcConnect);
        String dc = line.getString(ChargePredictFields.ins().srcFieldBmsDcConnect);
        //车辆vin、地点、电流、起点soc、电池温度、目标soc、加热、空调开启
        StringBuilder stringBuilder = new StringBuilder();
        dimension.rnDimStr(stringBuilder,modelCode);
        dimension.andOps(stringBuilder);
        dimension.startSocDim(stringBuilder,bmsSoc);
        dimension.andOps(stringBuilder);

        dimension.deviceIdDim(stringBuilder,vin,true);
        dimension.andOps(stringBuilder);
        boolean firstTime = line.getBooleanValue(ChargePredictFields.ins().firstTimeField);
        dimension.startTimeDim(stringBuilder,startChargeTime,firstTime);
        dimension.andOps(stringBuilder);
        dimension.socDim(stringBuilder,bmsSoc);
        //startsoc
        dimension.andOps(stringBuilder);
        dimension.tempDim(stringBuilder,tempMin);
        dimension.andOps(stringBuilder);
//        dimension.currDimObc(stringBuilder,bmsCurr,voltMax);
        dimension.currDimObc2(stringBuilder,bmsCurr,voltMax);
        dimension.andOps(stringBuilder);
        dimension.steadyTimeDim(stringBuilder);
        dimension.andOps(stringBuilder);
        dimension.dcObcDim(stringBuilder,obc,dc);
        if (hasPoiDim){
            dimension.poiDim(stringBuilder,poiId,aoiId);
        }
        dimension.orderByObc(stringBuilder);
        //拼接
        return stringBuilder.toString();
    }
    private String queryOptionModel(JSONObject line,boolean hasPoiDim){
        //获取当前工况
        String vin = line.getString(ChargePredictFields.ins().srcFieldVin);
        Long tidVal = line.getLong(ChargePredictFields.ins().srcFieldTid);
        String startChargeTime = line.getString(ChargePredictFields.ins().srcFieldStartChargeTime);
        String poiId = line.getString(ChargePredictFields.ins().srcFieldPoiId);
        String aoiId = line.getString(ChargePredictFields.ins().srcFieldAoiId);
        String tempMin = line.getString(ChargePredictFields.ins().srcFieldBmsRmcModuleTempMin);
        String tempMax = line.getString(ChargePredictFields.ins().srcFieldBmsRmcModuleTempMax);
        Float bmsCurr = line.getFloat(ChargePredictFields.ins().srcFieldBmsBatterCurrRm);
        String voltMax = line.getString(ChargePredictFields.ins().srcFieldVoltMax);
        Float bmsSoc = line.getFloat(ChargePredictFields.ins().srcFieldBmsSoc);
        String modelCode = line.getString(ChargePredictFields.ins().srcFieldModelCode);
        String acOpen = line.getString(ChargePredictFields.ins().srcFieldAcOpen);
        String heat = line.getString(ChargePredictFields.ins().srcFieldBmsHeatReq);
        String cool = line.getString(ChargePredictFields.ins().srcFieldBmsCoolReq);
        String obc = line.getString(ChargePredictFields.ins().srcFieldBmsObcConnect);
        String dc = line.getString(ChargePredictFields.ins().srcFieldBmsDcConnect);
        //车辆vin、地点、电流、起点soc、电池温度、目标soc、加热、空调开启
        StringBuilder stringBuilder = new StringBuilder();
        dimension.rnDimStr(stringBuilder,modelCode);
        dimension.andOps(stringBuilder);
        dimension.startSocDim(stringBuilder,bmsSoc);
        dimension.andOps(stringBuilder);

        dimension.modelDim(stringBuilder,modelCode);
        dimension.andOps(stringBuilder);

        //startsoc
        dimension.socDim(stringBuilder,bmsSoc);
        dimension.andOps(stringBuilder);
        dimension.tempDim(stringBuilder,tempMin);
        dimension.andOps(stringBuilder);
//        dimension.currDimObc(stringBuilder,bmsCurr,voltMax);
        dimension.currDimObc2(stringBuilder,bmsCurr,voltMax);
        boolean firstTime = line.getBooleanValue(ChargePredictFields.ins().firstTimeField);
        dimension.andOps(stringBuilder);
        dimension.startTimeDim(stringBuilder,startChargeTime,firstTime);
        dimension.andOps(stringBuilder);
        dimension.steadyTimeDim(stringBuilder);
        dimension.andOps(stringBuilder);
        dimension.dcObcDim(stringBuilder,obc,dc);
        if (hasPoiDim){
            dimension.poiDim(stringBuilder,poiId,aoiId);    
        }
        dimension.orderByObcDefault(stringBuilder);
        //拼接
        return stringBuilder.toString();
    }
    private String selectStr = "select regexp_split(id,\'-\')[1] as \"deviceid\",reverse(regexp_split(id,\'-\')[2]) as \"item_time\",\"bms_innersoc\",\"startchargetime\",\"bms_rmc_moduletempmin\",\"totaltime\",\"model_code\",\"bms_battcurr_rm\",\"steady_soc_10\",\"steady_time_10\",\"steady_soc_80\",\"steady_time_80\",\"poiid\",\"aoiid\",\"endchargetime\",\"end_bms_innersoc\",\"effectivechargetime\",\"startsoc\" ,abs(to_number(\"startsoc\")- %s) as \"deltaSocAbs\",(to_number(\"startsoc\")- %s) as \"deltaSoc\" ";
    private String fromStr =" from \"vaas_dws\".\"%s\"  where ";
    private String fromStrModel =" from \"vaas_dws\".\"%s\"  where ";
    private CloudMatchDimension dimension = new CloudMatchDimension();

    private List<JSONObject> queryExecution(String sqlStr,JSONObject line) {
        //查一个曲线的开始点 一条曲线只需要查找开始点，之后根据开始点查找结束点，这样就是一条曲线
        Connection connection = null;
        ArrayList<JSONObject> list = new ArrayList<>();
        try {
            String sql = sqlStr;
            String soc = line.getString(ChargePredictFields.ins().srcFieldBmsSoc);
            System.out.println(String.format("chargeId=%s ,soc=%s,sql_of_match ---> %s ",chargeIdName(line),soc,sql));
            connection = HBaseDataSource.ins().getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            ResultSet rs = null;
            try {
                rs = preparedStatement.executeQuery();
            }catch (Exception e){
                //第一次如果报错了，那么就重试一次，如果还是错误先跳过
                rs = preparedStatement.executeQuery();
            }
            HashSet<String> uniqSet = new HashSet<>();
            while (rs.next()) {
                String deviceId = rs.getString(1);
                //如果deviceid是空，
                if (StringUtils.isBlank(deviceId)){
                    continue;
                }
                String itemTime = rs.getString(2);
                String bmsInnersoc = rs.getString(3);
                String startchargetime = rs.getString(4);
                String uniqKey = deviceId.concat(startchargetime);
                //保留:一个deviceid 一次充电记录 保留一个
                if(uniqSet.contains(uniqKey)){
                    continue;
                }
                uniqSet.add(uniqKey);
                String bmsRmcModuletempmin = rs.getString(5);
                String e = rs.getString(6);
                if (e==null || StringUtils.isBlank(e)){continue;}
                String modelCode = rs.getString(7);
                String bmsBattcurrRm = rs.getString(8);
                String steadySoc10 = rs.getString(9);

                String steadyTime10 = rs.getString(10);
                String steadySoc80 = rs.getString(11);
                String steadyTime80 = rs.getString(12);
                if(StringUtils.isBlank(steadyTime10) || StringUtils.isBlank(steadyTime80)){
                    continue;
                }
                String poiid = rs.getString(13);
                String aoiid = rs.getString(14);
                String steadyTimeEnd = rs.getString(15);
                String steadySocEnd = rs.getString(16);
                String effectiveChargeTime = rs.getString(17);
                String startsoc = rs.getString(18);
                JSONObject json = new JSONObject();
                json.put(deviceIdKey,deviceId);
                json.put(itemTimeKey,itemTime);
                json.put(innerSocKey,bmsInnersoc);
                json.put(startChargeTimeKey,startchargetime);
                json.put(acAmbKey,bmsRmcModuletempmin);
                json.put(sliceTimeKey,e);
                json.put(modelCodeKey,modelCode);
                json.put(currKey,bmsBattcurrRm);
                json.put(steadySoc10Key,steadySoc10);
                json.put(steadyTime10Key,steadyTime10);
                json.put(steadySoc80Key,steadySoc80);
                json.put(steadyTime80Key,steadyTime80);
                json.put(poiidKey,poiid);
                json.put(aoiidKey,aoiid);
                json.put(steadySocEndKey,steadySocEnd);
                json.put(steadyTimeEndKey,steadyTimeEnd);
                json.put(effectiveChargeTimeKey,effectiveChargeTime);
                json.put(startSocKey,startsoc);
                list.add(json);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            HBaseDataSource.ins().close(connection);
        }
        return list;
    }

    /**
     * 地点维度
     * @param line
     * @param list
     * @return
     */
    private JSONObject poiIdDimension(JSONObject line,List<JSONObject> list) {
        //将同一个地点 同一个vin 电流相同的记录找出
        if (list==null || list.size()==0){
            return null;
        }
        JSONObject jsonObject = list.get(0);
        return jsonObject;
    }
    private String deviceIdKey = "deviceId";
    private String itemTimeKey = "item_time";
    private String innerSocKey = "bms_innersoc";
    private String startChargeTimeKey = "startChargeTime";
    private String acAmbKey = "bms_rmc_moduletempmin";
    private String sliceTimeKey = "totaltime";
    private String modelCodeKey = "model_code";
    private String currKey = "bms_battcurr_rm";
    private String steadySoc10Key = "steady_soc_10";
    private String steadyTime10Key = "steady_time_10";
    private String steadySoc80Key = "steady_soc_80";
    private String steadyTime80Key = "steady_time_80";
    private String poiidKey = "poiid";
    private String aoiidKey = "aoiid";
    private String steadySocEndKey = "end_bms_innersoc";
    private String steadyTimeEndKey = "endchargetime";
    private String effectiveChargeTimeKey = "effectivechargetime";
    private String startSocKey = "startsoc";

}
